Introduce GtkPopover
authorCarlos Garnacho <carlosg@gnome.org>
Tue, 12 Nov 2013 15:55:23 +0000 (16:55 +0100)
committerCarlos Garnacho <carlosg@gnome.org>
Wed, 22 Jan 2014 16:10:04 +0000 (17:10 +0100)
Now that the GtkBubbleWindow object has been cleaned up and made
more generic, rename it as GtkPopover and make it public.

gtk/Makefile.am
gtk/gtk.h
gtk/gtkbubblewindow.c [deleted file]
gtk/gtkbubblewindowprivate.h [deleted file]
gtk/gtkentry.c
gtk/gtkpopover.c [new file with mode: 0644]
gtk/gtkpopover.h [new file with mode: 0644]
gtk/gtktextview.c

index 06b54ede5a77ea3a3d83c9381c7b5e75d1005002..813fb91069d183d2c8876899439d5a60b9f6fd14 100644 (file)
@@ -296,6 +296,7 @@ gtk_public_h_sources =              \
        gtkpapersize.h          \
        gtkplacessidebar.h      \
        gtkplug.h               \
+       gtkpopover.h            \
        gtkprintcontext.h       \
        gtkprintoperation.h     \
        gtkprintoperationpreview.h \
@@ -411,7 +412,6 @@ gtk_private_h_sources =             \
        gtkbookmarksmanager.h   \
        gtkborderimageprivate.h \
        gtkboxprivate.h         \
-       gtkbubblewindowprivate.h        \
        gtkbuilderprivate.h     \
        gtkbuttonprivate.h      \
        gtkcairoblurprivate.h   \
@@ -629,7 +629,6 @@ gtk_base_c_sources =                \
        gtkborder.c             \
        gtkborderimage.c        \
        gtkbox.c                \
-       gtkbubblewindow.c       \
        gtkbuildable.c          \
        gtkbuilder.c            \
        gtkbuilderparser.c      \
@@ -801,6 +800,7 @@ gtk_base_c_sources =                \
        gtkprivatetypebuiltins.c \
        gtkprogressbar.c        \
        gtkpixelcache.c         \
+       gtkpopover.c            \
        gtkradiobutton.c        \
        gtkradiomenuitem.c      \
        gtkradiotoolbutton.c    \
index 186f9a06375daf34c64a49a9adb1042334fae70b..16357c148606f4415a7662c7b4dd3e258d03c121 100644 (file)
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
 #include <gtk/gtkpapersize.h>
 #include <gtk/gtkpaned.h>
 #include <gtk/gtkplacessidebar.h>
+#include <gtk/gtkpopover.h>
 #include <gtk/gtkprintcontext.h>
 #include <gtk/gtkprintoperation.h>
 #include <gtk/gtkprintoperationpreview.h>
diff --git a/gtk/gtkbubblewindow.c b/gtk/gtkbubblewindow.c
deleted file mode 100644 (file)
index f0b325b..0000000
+++ /dev/null
@@ -1,1086 +0,0 @@
-/* GTK - The GIMP Toolkit
- * Copyright © 2013 Carlos Garnacho <carlosg@gnome.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-/*
- * GtkBubbleWindow is a bubble-like context window, primarily mean for
- * context-dependent helpers on touch interfaces.
- *
- * In order to place a GtkBubbleWindow to point to some other area,
- * use gtk_bubble_window_set_relative_to(), gtk_bubble_window_set_pointing_to()
- * and gtk_bubble_window_set_position(). Although it is usually  more
- * convenient to use gtk_bubble_window_popup() which handles all of those
- * at once.
- *
- * By default, no grabs are performed on the GtkBubbleWindow, leaving
- * the popup/popdown semantics up to the caller, gtk_bubble_window_grab()
- * can be used to grab the window for a device pair, bringing #GtkMenu alike
- * popdown behavior by default on keyboard/pointer interaction. Grabs need
- * to be undone through gtk_bubble_window_ungrab().
- */
-
-#include "config.h"
-#include <gdk/gdk.h>
-#include <cairo-gobject.h>
-#include "gtkbubblewindowprivate.h"
-#include "gtktypebuiltins.h"
-#include "gtkmain.h"
-#include "gtkprivate.h"
-#include "gtkintl.h"
-
-#define TAIL_GAP_WIDTH 24
-#define TAIL_HEIGHT    12
-
-#define POS_IS_VERTICAL(p) ((p) == GTK_POS_TOP || (p) == GTK_POS_BOTTOM)
-
-typedef struct _GtkBubbleWindowPrivate GtkBubbleWindowPrivate;
-
-enum {
-  PROP_RELATIVE_TO = 1,
-  PROP_POINTING_TO,
-  PROP_POSITION
-};
-
-struct _GtkBubbleWindowPrivate
-{
-  GdkDevice *device;
-  GtkWidget *widget;
-  GtkWindow *window;
-  cairo_rectangle_int_t pointing_to;
-  gint win_x;
-  gint win_y;
-  guint has_pointing_to    : 1;
-  guint preferred_position : 2;
-  guint final_position     : 2;
-};
-
-G_DEFINE_TYPE_WITH_PRIVATE (GtkBubbleWindow, _gtk_bubble_window, GTK_TYPE_BIN)
-
-static void
-_gtk_bubble_window_init (GtkBubbleWindow *window)
-{
-  GtkWidget *widget;
-
-  widget = GTK_WIDGET (window);
-  gtk_widget_set_has_window (widget, TRUE);
-  window->priv = _gtk_bubble_window_get_instance_private (window);
-  gtk_style_context_add_class (gtk_widget_get_style_context (widget),
-                               GTK_STYLE_CLASS_OSD);
-}
-
-static void
-gtk_bubble_window_set_property (GObject      *object,
-                                guint         prop_id,
-                                const GValue *value,
-                                GParamSpec   *pspec)
-{
-  switch (prop_id)
-    {
-    case PROP_RELATIVE_TO:
-      _gtk_bubble_window_set_relative_to (GTK_BUBBLE_WINDOW (object),
-                                          g_value_get_object (value));
-      break;
-    case PROP_POINTING_TO:
-      _gtk_bubble_window_set_pointing_to (GTK_BUBBLE_WINDOW (object),
-                                          g_value_get_boxed (value));
-      break;
-    case PROP_POSITION:
-      _gtk_bubble_window_set_position (GTK_BUBBLE_WINDOW (object),
-                                       g_value_get_enum (value));
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
-gtk_bubble_window_get_property (GObject    *object,
-                                guint       prop_id,
-                                GValue     *value,
-                                GParamSpec *pspec)
-{
-  GtkBubbleWindowPrivate *priv = GTK_BUBBLE_WINDOW (object)->priv;
-
-  switch (prop_id)
-    {
-    case PROP_RELATIVE_TO:
-      g_value_set_object (value, priv->widget);
-      break;
-    case PROP_POINTING_TO:
-      g_value_set_boxed (value, &priv->pointing_to);
-      break;
-    case PROP_POSITION:
-      g_value_set_enum (value, priv->preferred_position);
-      break;
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
-
-static void
-gtk_bubble_window_finalize (GObject *object)
-{
-  GtkBubbleWindow *window = GTK_BUBBLE_WINDOW (object);
-
-  G_OBJECT_CLASS (_gtk_bubble_window_parent_class)->finalize (object);
-}
-
-static void
-gtk_bubble_window_dispose (GObject *object)
-{
-  GtkBubbleWindow *window = GTK_BUBBLE_WINDOW (object);
-  GtkBubbleWindowPrivate *priv = window->priv;
-
-  if (priv->window)
-    gtk_window_remove_popover (priv->window, GTK_WIDGET (object));
-
-  priv->window = NULL;
-  priv->widget = NULL;
-
-  G_OBJECT_CLASS (_gtk_bubble_window_parent_class)->dispose (object);
-}
-
-static void
-gtk_bubble_window_realize (GtkWidget *widget)
-{
-  GtkAllocation allocation;
-  GdkWindowAttr attributes;
-  gint attributes_mask;
-  GdkWindow *window;
-
-  gtk_widget_get_allocation (widget, &allocation);
-
-  attributes.x = 0;
-  attributes.y = 0;
-  attributes.width = allocation.width;
-  attributes.height = allocation.height;
-  attributes.window_type = GDK_WINDOW_CHILD;
-  attributes.visual = gtk_widget_get_visual (widget);
-  attributes.wclass = GDK_INPUT_OUTPUT;
-  attributes.event_mask =
-    gtk_widget_get_events (widget) |
-    GDK_BUTTON_MOTION_MASK |
-    GDK_BUTTON_PRESS_MASK |
-    GDK_BUTTON_RELEASE_MASK |
-    GDK_EXPOSURE_MASK |
-    GDK_ENTER_NOTIFY_MASK |
-    GDK_LEAVE_NOTIFY_MASK;
-
-  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
-  window = gdk_window_new (gtk_widget_get_parent_window (widget),
-                           &attributes, attributes_mask);
-  gtk_widget_set_window (widget, window);
-  gtk_widget_register_window (widget, window);
-  gtk_widget_set_realized (widget, TRUE);
-}
-
-static void
-gtk_bubble_window_map (GtkWidget *widget)
-{
-  gdk_window_show (gtk_widget_get_window (widget));
-  GTK_WIDGET_CLASS (_gtk_bubble_window_parent_class)->map (widget);
-}
-
-static void
-gtk_bubble_window_unmap (GtkWidget *widget)
-{
-  gdk_window_hide (gtk_widget_get_window (widget));
-  GTK_WIDGET_CLASS (_gtk_bubble_window_parent_class)->unmap (widget);
-}
-
-static void
-gtk_bubble_window_get_pointed_to_coords (GtkBubbleWindow       *window,
-                                         gint                  *x,
-                                         gint                  *y,
-                                         cairo_rectangle_int_t *rect_out)
-{
-  GtkBubbleWindowPrivate *priv = window->priv;
-  cairo_rectangle_int_t rect;
-  GtkAllocation window_alloc;
-
-  _gtk_bubble_window_get_pointing_to (window, &rect);
-  gtk_widget_get_allocation (GTK_WIDGET (priv->window), &window_alloc);
-  gtk_widget_translate_coordinates (priv->widget, GTK_WIDGET (priv->window),
-                                    rect.x, rect.y, &rect.x, &rect.y);
-
-  if (POS_IS_VERTICAL (priv->final_position))
-    {
-      *x = CLAMP (rect.x + (rect.width / 2), 0, window_alloc.width);
-      *y = rect.y;
-
-      if (priv->final_position == GTK_POS_BOTTOM)
-        (*y) += rect.height;
-    }
-  else
-    {
-      *y = CLAMP (rect.y + (rect.height / 2), 0, window_alloc.height);
-      *x = rect.x;
-
-      if (priv->final_position == GTK_POS_RIGHT)
-        (*x) += rect.width;
-    }
-
-  if (rect_out)
-    *rect_out = rect;
-}
-
-static void
-gtk_bubble_window_get_gap_coords (GtkBubbleWindow *window,
-                                  gint            *initial_x_out,
-                                  gint            *initial_y_out,
-                                  gint            *tip_x_out,
-                                  gint            *tip_y_out,
-                                  gint            *final_x_out,
-                                  gint            *final_y_out,
-                                  GtkPositionType *gap_side_out)
-{
-  GtkBubbleWindowPrivate *priv = window->priv;
-  gint base, tip, x, y;
-  gint initial_x, initial_y;
-  gint tip_x, tip_y;
-  gint final_x, final_y;
-  GtkPositionType gap_side;
-  GtkAllocation allocation;
-  gint border_radius;
-
-  gtk_bubble_window_get_pointed_to_coords (window, &x, &y, NULL);
-  gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
-
-  gtk_style_context_get (gtk_widget_get_style_context (GTK_WIDGET (window)),
-                         gtk_widget_get_state_flags (GTK_WIDGET (window)),
-                         GTK_STYLE_PROPERTY_BORDER_RADIUS, &border_radius,
-                         NULL);
-
-  base = tip = 0;
-  gap_side = GTK_POS_LEFT;
-
-  if (priv->final_position == GTK_POS_BOTTOM ||
-      priv->final_position == GTK_POS_RIGHT)
-    {
-      base = TAIL_HEIGHT;
-      tip = 0;
-
-      gap_side = (priv->final_position == GTK_POS_BOTTOM) ? GTK_POS_TOP : GTK_POS_LEFT;
-    }
-  else if (priv->final_position == GTK_POS_TOP)
-    {
-      base = allocation.height - TAIL_HEIGHT;
-      tip = allocation.height;
-      gap_side = GTK_POS_BOTTOM;
-    }
-  else if (priv->final_position == GTK_POS_LEFT)
-    {
-      base = allocation.width - TAIL_HEIGHT;
-      tip = allocation.width;
-      gap_side = GTK_POS_RIGHT;
-    }
-
-  if (POS_IS_VERTICAL (priv->final_position))
-    {
-      initial_x = CLAMP (x - priv->win_x - TAIL_GAP_WIDTH / 2,
-                         border_radius, allocation.width - TAIL_GAP_WIDTH);
-      initial_y = base;
-
-      tip_x = CLAMP (x - priv->win_x, 0, allocation.width);
-      tip_y = tip;
-
-      final_x = CLAMP (x - priv->win_x + TAIL_GAP_WIDTH / 2,
-                       TAIL_GAP_WIDTH, allocation.width - border_radius);
-      final_y = base;
-    }
-  else
-    {
-      initial_x = base;
-      initial_y = CLAMP (y - priv->win_y - TAIL_GAP_WIDTH / 2,
-                         border_radius, allocation.height - TAIL_GAP_WIDTH);
-
-      tip_x = tip;
-      tip_y = CLAMP (y - priv->win_y, 0, allocation.height);
-
-      final_x = base;
-      final_y = CLAMP (y - priv->win_y + TAIL_GAP_WIDTH / 2,
-                       TAIL_GAP_WIDTH, allocation.height - border_radius);
-    }
-
-  if (initial_x_out)
-    *initial_x_out = initial_x;
-  if (initial_y_out)
-    *initial_y_out = initial_y;
-
-  if (tip_x_out)
-    *tip_x_out = tip_x;
-  if (tip_y_out)
-    *tip_y_out = tip_y;
-
-  if (final_x_out)
-    *final_x_out = final_x;
-  if (final_y_out)
-    *final_y_out = final_y;
-
-  if (gap_side_out)
-    *gap_side_out = gap_side;
-}
-
-static void
-gtk_bubble_window_get_rect_coords (GtkBubbleWindow *window,
-                                   gint            *x1_out,
-                                   gint            *y1_out,
-                                   gint            *x2_out,
-                                   gint            *y2_out)
-{
-  GtkBubbleWindowPrivate *priv = window->priv;
-  gint x1, x2, y1, y2;
-  GtkAllocation allocation;
-
-  x1 = y1 = x2 = y2 = 0;
-  gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
-
-  if (priv->final_position == GTK_POS_TOP)
-    {
-      x1 = 0;
-      y1 = 0;
-      x2 = allocation.width;
-      y2 = allocation.height - TAIL_HEIGHT;
-    }
-  else if (priv->final_position == GTK_POS_BOTTOM)
-    {
-      x1 = 0;
-      y1 = TAIL_HEIGHT;
-      x2 = allocation.width;
-      y2 = allocation.height;
-    }
-  else if (priv->final_position == GTK_POS_LEFT)
-    {
-      x1 = 0;
-      y1 = 0;
-      x2 = allocation.width - TAIL_HEIGHT;
-      y2 = allocation.height;
-    }
-  else if (priv->final_position == GTK_POS_RIGHT)
-    {
-      x1 = TAIL_HEIGHT;
-      y1 = 0;
-      x2 = allocation.width;
-      y2 = allocation.height;
-    }
-
-  if (x1_out)
-    *x1_out = x1;
-  if (y1_out)
-    *y1_out = y1;
-  if (x2_out)
-    *x2_out = x2;
-  if (y2_out)
-    *y2_out = y2;
-}
-
-static void
-gtk_bubble_window_apply_tail_path (GtkBubbleWindow *window,
-                                   cairo_t         *cr)
-{
-  gint initial_x, initial_y;
-  gint tip_x, tip_y;
-  gint final_x, final_y;
-
-  gtk_bubble_window_get_gap_coords (window,
-                                    &initial_x, &initial_y,
-                                    &tip_x, &tip_y,
-                                    &final_x, &final_y,
-                                    NULL);
-
-  cairo_move_to (cr, initial_x, initial_y);
-  cairo_line_to (cr, tip_x, tip_y);
-  cairo_line_to (cr, final_x, final_y);
-}
-
-static void
-gtk_bubble_window_apply_border_path (GtkBubbleWindow *window,
-                                     cairo_t         *cr)
-{
-  GtkBubbleWindowPrivate *priv;
-  GtkAllocation allocation;
-  gint x1, y1, x2, y2;
-
-  priv = window->priv;
-  gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
-
-  gtk_bubble_window_apply_tail_path (window, cr);
-  gtk_bubble_window_get_rect_coords (window, &x1, &y1, &x2, &y2);
-
-  if (priv->final_position == GTK_POS_TOP)
-    {
-      cairo_line_to (cr, x2, y2);
-      cairo_line_to (cr, x2, y1);
-      cairo_line_to (cr, x1, y1);
-      cairo_line_to (cr, x1, y2);
-    }
-  else if (priv->final_position == GTK_POS_BOTTOM)
-    {
-      cairo_line_to (cr, x2, y1);
-      cairo_line_to (cr, x2, y2);
-      cairo_line_to (cr, x1, y2);
-      cairo_line_to (cr, x1, y1);
-    }
-  else if (priv->final_position == GTK_POS_LEFT)
-    {
-      cairo_line_to (cr, x2, y2);
-      cairo_line_to (cr, x1, y2);
-      cairo_line_to (cr, x1, y1);
-      cairo_line_to (cr, x2, y1);
-    }
-  else if (priv->final_position == GTK_POS_RIGHT)
-    {
-      cairo_line_to (cr, x1, y1);
-      cairo_line_to (cr, x2, y1);
-      cairo_line_to (cr, x2, y2);
-      cairo_line_to (cr, x1, y2);
-    }
-
-  cairo_close_path (cr);
-}
-
-static void
-gtk_bubble_window_update_shape (GtkBubbleWindow *window)
-{
-  cairo_surface_t *surface;
-  cairo_region_t *region;
-  GdkWindow *win;
-  cairo_t *cr;
-
-  win = gtk_widget_get_window (GTK_WIDGET (window));
-  surface =
-    gdk_window_create_similar_surface (win,
-                                       CAIRO_CONTENT_COLOR_ALPHA,
-                                       gdk_window_get_width (win),
-                                       gdk_window_get_height (win));
-
-  cr = cairo_create (surface);
-  gtk_bubble_window_apply_border_path (window, cr);
-  cairo_fill (cr);
-  cairo_destroy (cr);
-
-  region = gdk_cairo_region_create_from_surface (surface);
-  cairo_surface_destroy (surface);
-
-  gtk_widget_shape_combine_region (GTK_WIDGET (window), region);
-  cairo_region_destroy (region);
-}
-
-static void
-gtk_bubble_window_update_position (GtkBubbleWindow *window)
-{
-  GtkAllocation allocation, window_alloc;
-  GtkBubbleWindowPrivate *priv;
-  cairo_rectangle_int_t rect;
-  gint win_x, win_y, x, y;
-
-  priv = window->priv;
-  gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
-  gtk_widget_get_allocation (GTK_WIDGET (priv->window), &window_alloc);
-  priv->final_position = priv->preferred_position;
-  _gtk_bubble_window_get_pointing_to (window, &rect);
-
-  gtk_bubble_window_get_pointed_to_coords (window, &x, &y, &rect);
-
-  /* Check whether there's enough room on the
-   * preferred side, move to the opposite one if not.
-   */
-  if (priv->preferred_position == GTK_POS_TOP && rect.y < allocation.height)
-    priv->final_position = GTK_POS_BOTTOM;
-  else if (priv->preferred_position == GTK_POS_BOTTOM &&
-           rect.y > window_alloc.height - allocation.height)
-    priv->final_position = GTK_POS_TOP;
-  else if (priv->preferred_position == GTK_POS_LEFT && rect.x < allocation.width)
-    priv->final_position = GTK_POS_RIGHT;
-  else if (priv->preferred_position == GTK_POS_RIGHT &&
-           rect.x > window_alloc.width - allocation.width)
-    priv->final_position = GTK_POS_LEFT;
-
-  if (POS_IS_VERTICAL (priv->final_position))
-    {
-      win_x = CLAMP (x - allocation.width / 2,
-                     0, window_alloc.width - allocation.width);
-      win_y = y;
-
-      if (priv->final_position == GTK_POS_TOP)
-        win_y -= allocation.height;
-      else if (priv->final_position == GTK_POS_BOTTOM)
-        win_y += rect.height;
-    }
-  else
-    {
-      win_y = CLAMP (y - allocation.height / 2,
-                    0, window_alloc.height - allocation.height);
-      win_x = x;
-
-      if (priv->final_position == GTK_POS_LEFT)
-        win_x -= allocation.width;
-      else if (priv->final_position == GTK_POS_RIGHT)
-        win_x += rect.width;
-    }
-
-  priv->win_x = win_x;
-  priv->win_y = win_y;
-
-  gtk_window_set_popover_position (priv->window,
-                                   GTK_WIDGET (window),
-                                   win_x, win_y);
-
-  gtk_bubble_window_update_shape (window);
-}
-
-static gboolean
-gtk_bubble_window_draw (GtkWidget *widget,
-                        cairo_t   *cr)
-{
-  GtkStyleContext *context;
-  GtkAllocation allocation;
-  GtkWidget *child;
-  GtkBorder border;
-  GdkRGBA border_color;
-  gint rect_x1, rect_x2, rect_y1, rect_y2;
-  gint initial_x, initial_y, final_x, final_y;
-  gint gap_start, gap_end;
-  GtkPositionType gap_side;
-  GtkStateFlags state;
-
-  context = gtk_widget_get_style_context (widget);
-  state = gtk_widget_get_state_flags (widget);
-  gtk_widget_get_allocation (widget, &allocation);
-
-  gtk_bubble_window_get_rect_coords (GTK_BUBBLE_WINDOW (widget),
-                                     &rect_x1, &rect_y1,
-                                     &rect_x2, &rect_y2);
-
-  /* Render the rect background */
-  gtk_render_background (context, cr,
-                         rect_x1, rect_y1,
-                         rect_x2 - rect_x1, rect_y2 - rect_y1);
-
-  gtk_bubble_window_get_gap_coords (GTK_BUBBLE_WINDOW (widget),
-                                    &initial_x, &initial_y,
-                                    NULL, NULL,
-                                    &final_x, &final_y,
-                                    &gap_side);
-
-  if (POS_IS_VERTICAL (gap_side))
-    {
-      gap_start = initial_x;
-      gap_end = final_x;
-    }
-  else
-    {
-      gap_start = initial_y;
-      gap_end = final_y;
-    }
-
-  /* Now render the frame, without the gap for the arrow tip */
-  gtk_render_frame_gap (context, cr,
-                        rect_x1, rect_y1,
-                        rect_x2 - rect_x1, rect_y2 - rect_y1,
-                        gap_side,
-                        gap_start, gap_end);
-
-  /* Clip to the arrow shape */
-  cairo_save (cr);
-
-  gtk_bubble_window_apply_tail_path (GTK_BUBBLE_WINDOW (widget), cr);
-  cairo_clip (cr);
-
-  /* Render the arrow background */
-  gtk_render_background (context, cr,
-                         0, 0,
-                         allocation.width, allocation.height);
-
-  /* Render the border of the arrow tip */
-  gtk_style_context_get_border (context, state, &border);
-
-  if (border.bottom > 0)
-    {
-      gtk_style_context_get_border_color (context, state, &border_color);
-      gtk_bubble_window_apply_tail_path (GTK_BUBBLE_WINDOW (widget), cr);
-      gdk_cairo_set_source_rgba (cr, &border_color);
-
-      cairo_set_line_width (cr, border.bottom);
-      cairo_stroke (cr);
-    }
-
-  /* We're done */
-  cairo_restore (cr);
-
-  child = gtk_bin_get_child (GTK_BIN (widget));
-
-  if (child)
-    gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
-
-  return TRUE;
-}
-
-static void
-get_padding_and_border (GtkWidget *widget,
-                        GtkBorder *border)
-{
-  GtkStyleContext *context;
-  GtkStateFlags state;
-  GtkBorder tmp;
-
-  context = gtk_widget_get_style_context (widget);
-  state = gtk_widget_get_state_flags (widget);
-
-  gtk_style_context_get_padding (context, state, border);
-  gtk_style_context_get_border (context, state, &tmp);
-  border->top += tmp.top;
-  border->right += tmp.right;
-  border->bottom += tmp.bottom;
-  border->left += tmp.left;
-}
-
-static void
-gtk_bubble_window_get_preferred_width (GtkWidget *widget,
-                                       gint      *minimum_width,
-                                       gint      *natural_width)
-{
-  GtkBubbleWindowPrivate *priv;
-  GtkWidget *child;
-  gint min, nat;
-  GtkBorder border;
-
-  priv = GTK_BUBBLE_WINDOW (widget)->priv;
-  child = gtk_bin_get_child (GTK_BIN (widget));
-  min = nat = 0;
-
-  if (child)
-    gtk_widget_get_preferred_width (child, &min, &nat);
-
-  get_padding_and_border (widget, &border);
-  min += border.left + border.right;
-  nat += border.left + border.right;
-
-  if (!POS_IS_VERTICAL (priv->final_position))
-    {
-      min += TAIL_HEIGHT;
-      nat += TAIL_HEIGHT;
-    }
-
-  if (minimum_width)
-    *minimum_width = MAX (min, TAIL_GAP_WIDTH);
-
-  if (natural_width)
-    *natural_width = MAX (nat, TAIL_GAP_WIDTH);
-}
-
-static void
-gtk_bubble_window_get_preferred_height (GtkWidget *widget,
-                                        gint      *minimum_height,
-                                        gint      *natural_height)
-{
-  GtkBubbleWindowPrivate *priv;
-  GtkWidget *child;
-  gint min, nat;
-  GtkBorder border;
-
-  priv = GTK_BUBBLE_WINDOW (widget)->priv;
-  child = gtk_bin_get_child (GTK_BIN (widget));
-  min = nat = 0;
-
-  if (child)
-    gtk_widget_get_preferred_height (child, &min, &nat);
-
-  get_padding_and_border (widget, &border);
-  min += border.top + border.bottom;
-  nat += border.top + border.bottom;
-
-  if (POS_IS_VERTICAL (priv->final_position))
-    {
-      min += TAIL_HEIGHT;
-      nat += TAIL_HEIGHT;
-    }
-
-  if (minimum_height)
-    *minimum_height = MAX (min, TAIL_GAP_WIDTH);
-
-  if (natural_height)
-    *natural_height = MAX (nat, TAIL_GAP_WIDTH);
-}
-
-static void
-gtk_bubble_window_size_allocate (GtkWidget     *widget,
-                                 GtkAllocation *allocation)
-{
-  GtkBubbleWindowPrivate *priv;
-  GtkWidget *child;
-
-  priv = GTK_BUBBLE_WINDOW (widget)->priv;
-  gtk_widget_set_allocation (widget, allocation);
-  child = gtk_bin_get_child (GTK_BIN (widget));
-
-  if (gtk_widget_get_visible (widget))
-    gtk_bubble_window_update_position (GTK_BUBBLE_WINDOW (widget));
-
-  if (child)
-    {
-      GtkAllocation child_alloc;
-      GtkBorder border;
-
-      get_padding_and_border (widget, &border);
-
-      child_alloc.x = allocation->x + border.left;
-      child_alloc.y = allocation->y + border.top;
-      child_alloc.width = allocation->width - border.left - border.right;
-      child_alloc.height = allocation->height - border.top - border.bottom;
-
-      if (POS_IS_VERTICAL (priv->final_position))
-        child_alloc.height -= TAIL_HEIGHT;
-      else
-        child_alloc.width -= TAIL_HEIGHT;
-
-      if (priv->final_position == GTK_POS_BOTTOM)
-        child_alloc.y += TAIL_HEIGHT;
-      else if (priv->final_position == GTK_POS_RIGHT)
-        child_alloc.x += TAIL_HEIGHT;
-
-      gtk_widget_size_allocate (child, &child_alloc);
-    }
-
-  if (gtk_widget_get_realized (widget))
-    {
-      gdk_window_move_resize (gtk_widget_get_window (widget),
-                              0, 0, allocation->width, allocation->height);
-      gtk_bubble_window_update_shape (GTK_BUBBLE_WINDOW (widget));
-    }
-}
-
-static gboolean
-gtk_bubble_window_button_press (GtkWidget      *widget,
-                                GdkEventButton *event)
-{
-  GtkWidget *child;
-
-  child = gtk_bin_get_child (GTK_BIN (widget));
-
-  if (child && event->window == gtk_widget_get_window (widget))
-    {
-      GtkAllocation child_alloc;
-
-      gtk_widget_get_allocation (child, &child_alloc);
-
-      if (event->x < child_alloc.x ||
-          event->x > child_alloc.x + child_alloc.width ||
-          event->y < child_alloc.y ||
-          event->y > child_alloc.y + child_alloc.height)
-        gtk_widget_hide (widget);
-    }
-  else
-    gtk_widget_hide (widget);
-
-  return GDK_EVENT_PROPAGATE;
-}
-
-static gboolean
-gtk_bubble_window_key_press (GtkWidget   *widget,
-                             GdkEventKey *event)
-{
-  if (event->keyval == GDK_KEY_Escape)
-    {
-      gtk_widget_hide (widget);
-      return GDK_EVENT_STOP;
-    }
-
-  return GDK_EVENT_PROPAGATE;
-}
-
-static void
-_gtk_bubble_window_class_init (GtkBubbleWindowClass *klass)
-{
-  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  object_class->set_property = gtk_bubble_window_set_property;
-  object_class->get_property = gtk_bubble_window_get_property;
-  object_class->finalize = gtk_bubble_window_finalize;
-  object_class->dispose = gtk_bubble_window_dispose;
-
-  widget_class->realize = gtk_bubble_window_realize;
-  widget_class->map = gtk_bubble_window_map;
-  widget_class->unmap = gtk_bubble_window_unmap;
-  widget_class->get_preferred_width = gtk_bubble_window_get_preferred_width;
-  widget_class->get_preferred_height = gtk_bubble_window_get_preferred_height;
-  widget_class->size_allocate = gtk_bubble_window_size_allocate;
-  widget_class->draw = gtk_bubble_window_draw;
-  widget_class->button_press_event = gtk_bubble_window_button_press;
-  widget_class->key_press_event = gtk_bubble_window_key_press;
-
-  g_object_class_install_property (object_class,
-                                   PROP_RELATIVE_TO,
-                                   g_param_spec_object ("relative-to",
-                                                        P_("Relative to"),
-                                                        P_("Window the bubble window points to"),
-                                                        GTK_TYPE_WIDGET,
-                                                        GTK_PARAM_READWRITE));
-  g_object_class_install_property (object_class,
-                                   PROP_POINTING_TO,
-                                   g_param_spec_boxed ("pointing-to",
-                                                       P_("Pointing to"),
-                                                       P_("Rectangle the bubble window points to"),
-                                                       CAIRO_GOBJECT_TYPE_RECTANGLE_INT,
-                                                       GTK_PARAM_READWRITE));
-  g_object_class_install_property (object_class,
-                                   PROP_POSITION,
-                                   g_param_spec_enum ("position",
-                                                      P_("Position"),
-                                                      P_("Position to place the bubble window"),
-                                                      GTK_TYPE_POSITION_TYPE, GTK_POS_TOP,
-                                                      GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-}
-
-static void
-gtk_bubble_window_update_relative_to (GtkBubbleWindow *window,
-                                      GtkWidget       *relative_to)
-{
-  GtkBubbleWindowPrivate *priv;
-
-  priv = window->priv;
-
-  if (priv->widget == relative_to)
-    return;
-
-  if (priv->window)
-    {
-      gtk_window_remove_popover (priv->window, GTK_WIDGET (window));
-      priv->window = NULL;
-    }
-
-  priv->widget = relative_to;
-  g_object_notify (G_OBJECT (window), "relative-to");
-
-  if (priv->widget)
-    priv->window =
-      GTK_WINDOW (gtk_widget_get_ancestor (priv->widget, GTK_TYPE_WINDOW));
-
-  if (priv->window)
-    gtk_window_add_popover (priv->window, GTK_WIDGET (window));
-}
-
-static void
-gtk_bubble_window_update_pointing_to (GtkBubbleWindow       *window,
-                                      cairo_rectangle_int_t *pointing_to)
-{
-  GtkBubbleWindowPrivate *priv;
-
-  priv = window->priv;
-
-  if (pointing_to)
-    {
-      priv->pointing_to = *pointing_to;
-      priv->has_pointing_to = TRUE;
-    }
-  else
-    priv->has_pointing_to = FALSE;
-
-  g_object_notify (G_OBJECT (window), "pointing-to");
-}
-
-static void
-gtk_bubble_window_update_preferred_position (GtkBubbleWindow *window,
-                                             GtkPositionType  position)
-{
-  GtkBubbleWindowPrivate *priv;
-
-  priv = window->priv;
-  priv->preferred_position = position;
-  g_object_notify (G_OBJECT (window), "position");
-}
-
-/*
- * gtk_bubble_window_new:
- *
- * Returns a newly created #GtkBubblewindow
- *
- * Returns: a new #GtkBubbleWindow
- *
- * Since: 3.8
- */
-GtkWidget *
-_gtk_bubble_window_new (GtkWidget *relative_to)
-{
-  return g_object_new (GTK_TYPE_BUBBLE_WINDOW,
-                       "relative-to", relative_to,
-                       NULL);
-}
-
-/*
- * gtk_bubble_window_set_relative_to:
- * @window: a #GtkBubbleWindow
- * @relative_to: a #GdkWindow
- *
- * Sets the #GdkWindow to act as the origin of coordinates of
- * @window, or %NULL to use the root window. See
- * gtk_bubble_window_set_pointing_to()
- *
- * If @window is currently visible, it will be moved to reflect
- * this change.
- *
- * Since: 3.8
- */
-void
-_gtk_bubble_window_set_relative_to (GtkBubbleWindow *window,
-                                    GtkWidget       *relative_to)
-{
-  g_return_if_fail (GTK_IS_BUBBLE_WINDOW (window));
-  g_return_if_fail (GTK_IS_WIDGET (relative_to));
-
-  gtk_bubble_window_update_relative_to (window, relative_to);
-
-  if (gtk_widget_get_visible (GTK_WIDGET (window)))
-    gtk_bubble_window_update_position (window);
-}
-
-/*
- * gtk_bubble_window_get_relative_to:
- * @window: a #GtkBubbleWindow
- *
- * Returns the #GdkWindow used as the origin of coordinates.
- * If @window is currently visible, it will be moved to reflect
- * this change.
- *
- * Returns: the #GdkWindow @window is placed upon
- *
- * Since: 3.8
- */
-GtkWidget *
-_gtk_bubble_window_get_relative_to (GtkBubbleWindow *window)
-{
-  GtkBubbleWindowPrivate *priv;
-
-  g_return_val_if_fail (GTK_IS_BUBBLE_WINDOW (window), NULL);
-
-  priv = window->priv;
-
-  return priv->widget;
-}
-
-/*
- * gtk_bubble_window_set_pointing_to:
- * @window: a #GtkBubbleWindow
- * @rect: rectangle to point to
- *
- * Sets the rectangle that @window will point to, the coordinates
- * of this rectangle are relative to the #GdkWindow set through
- * gtk_bubble_window_set_relative_to().
- *
- * Since: 3.8
- */
-void
-_gtk_bubble_window_set_pointing_to (GtkBubbleWindow       *window,
-                                    cairo_rectangle_int_t *rect)
-{
-  g_return_if_fail (GTK_IS_BUBBLE_WINDOW (window));
-  g_return_if_fail (rect != NULL);
-
-  gtk_bubble_window_update_pointing_to (window, rect);
-
-  if (gtk_widget_get_visible (GTK_WIDGET (window)))
-    gtk_bubble_window_update_position (window);
-}
-
-/*
- * gtk_bubble_window_get_pointing_to:
- * @window: a #GtkBubbleWindow
- * @rect: (out): location to store the rectangle
- *
- * If a rectangle to point to is set, this function will return
- * %TRUE and fill in @rect with the rectangle @window is currently
- * pointing to.
- *
- * Returns: %TRUE if a rectangle is set
- *
- * Since: 3.8
- */
-gboolean
-_gtk_bubble_window_get_pointing_to (GtkBubbleWindow       *window,
-                                    cairo_rectangle_int_t *rect)
-{
-  GtkBubbleWindowPrivate *priv;
-
-  g_return_val_if_fail (GTK_IS_BUBBLE_WINDOW (window), FALSE);
-
-  priv = window->priv;
-
-  if (rect)
-    {
-      if (priv->has_pointing_to)
-        *rect = priv->pointing_to;
-      else if (priv->widget)
-        {
-          gtk_widget_get_allocation (priv->widget, rect);
-          rect->x = rect->y = 0;
-        }
-    }
-
-  return priv->has_pointing_to;
-}
-
-/*
- * gtk_bubble_window_set_position:
- * @window: a #GtkBubbleWindow
- * @position: preferred bubble position
- *
- * Sets the preferred position for @window to appear.
- * If @window is currently visible, it will be moved to reflect
- * this change.
- *
- * <note>
- *   This preference will be respected where possible, although
- *   on lack of space (eg. if close to the screen edges), the
- *   #GtkBubbleWindow may choose to appear on the opposite side
- * </note>
- *
- * Since: 3.8
- */
-void
-_gtk_bubble_window_set_position (GtkBubbleWindow *window,
-                                 GtkPositionType  position)
-{
-  g_return_if_fail (GTK_IS_BUBBLE_WINDOW (window));
-  g_return_if_fail (position >= GTK_POS_LEFT && position <= GTK_POS_BOTTOM);
-
-  gtk_bubble_window_update_preferred_position (window, position);
-
-  if (gtk_widget_get_visible (GTK_WIDGET (window)))
-    gtk_bubble_window_update_position (window);
-}
-
-/*
- * gtk_bubble_window_get_position:
- * @window: a #GtkBubbleWindow
- *
- * Returns the preferred position to place @window
- *
- * Returns: The preferred position
- *
- * Since: 3.8
- */
-GtkPositionType
-_gtk_bubble_window_get_position (GtkBubbleWindow *window)
-{
-  GtkBubbleWindowPrivate *priv;
-
-  g_return_val_if_fail (GTK_IS_BUBBLE_WINDOW (window), GTK_POS_TOP);
-
-  priv = window->priv;
-
-  return priv->preferred_position;
-}
diff --git a/gtk/gtkbubblewindowprivate.h b/gtk/gtkbubblewindowprivate.h
deleted file mode 100644 (file)
index 0e57965..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/* GTK - The GIMP Toolkit
- * Copyright © 2013 Carlos Garnacho <carlosg@gnome.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __GTK_BUBBLE_WINDOW_H__
-#define __GTK_BUBBLE_WINDOW_H__
-
-#include <gtk/gtkwindow.h>
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_BUBBLE_WINDOW           (_gtk_bubble_window_get_type ())
-#define GTK_BUBBLE_WINDOW(o)             (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_BUBBLE_WINDOW, GtkBubbleWindow))
-#define GTK_BUBBLE_WINDOW_CLASS(c)       (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_BUBBLE_WINDOW, GtkBubbleWindowClass))
-#define GTK_IS_BUBBLE_WINDOW(o)          (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_BUBBLE_WINDOW))
-#define GTK_IS_BUBBLE_WINDOW_CLASS(o)    (G_TYPE_CHECK_CLASS_TYPE ((o), GTK_TYPE_BUBBLE_WINDOW))
-#define GTK_BUBBLE_WINDOW_GET_CLASS(o)   (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_BUBBLE_WINDOW, GtkBubbleWindowClass))
-
-typedef struct _GtkBubbleWindow GtkBubbleWindow;
-typedef struct _GtkBubbleWindowClass GtkBubbleWindowClass;
-
-struct _GtkBubbleWindow
-{
-  GtkBin parent_instance;
-
-  /*< private >*/
-  gpointer priv;
-};
-
-struct _GtkBubbleWindowClass
-{
-  GtkBinClass parent_class;
-};
-
-GType       _gtk_bubble_window_get_type        (void) G_GNUC_CONST;
-
-GtkWidget * _gtk_bubble_window_new             (GtkWidget             *relative_to);
-
-void        _gtk_bubble_window_set_relative_to (GtkBubbleWindow       *window,
-                                                GtkWidget             *relative_to);
-GtkWidget * _gtk_bubble_window_get_relative_to (GtkBubbleWindow       *window);
-
-void        _gtk_bubble_window_set_pointing_to (GtkBubbleWindow       *window,
-                                                cairo_rectangle_int_t *rect);
-gboolean    _gtk_bubble_window_get_pointing_to (GtkBubbleWindow       *window,
-                                                cairo_rectangle_int_t *rect);
-void        _gtk_bubble_window_set_position    (GtkBubbleWindow       *window,
-                                                GtkPositionType        position);
-GtkPositionType
-            _gtk_bubble_window_get_position    (GtkBubbleWindow       *window);
-
-G_END_DECLS
-
-#endif /* __GTK_BUBBLE_WINDOW_H__ */
index d457471aed89bffcd2896f599e0e74878f726449..b87bc06742135d31db482e913005d123926bc963 100644 (file)
@@ -63,7 +63,7 @@
 #include "gtkwidgetprivate.h"
 #include "gtkstylecontextprivate.h"
 #include "gtktexthandleprivate.h"
-#include "gtkbubblewindowprivate.h"
+#include "gtkpopover.h"
 #include "gtktoolbar.h"
 
 #include "a11y/gtkentryaccessible.h"
@@ -158,7 +158,7 @@ struct _GtkEntryPrivate
 
   gchar        *placeholder_text;
 
-  GtkBubbleWindow *bubble_window;
+  GtkWidget     *bubble_window;
   GtkTextHandle *text_handle;
   GtkWidget     *selection_bubble;
   guint          selection_bubble_timeout_id;
@@ -9405,9 +9405,9 @@ bubble_targets_received (GtkClipboard     *clipboard,
   if (priv->selection_bubble)
     gtk_widget_destroy (priv->selection_bubble);
 
-  priv->selection_bubble = _gtk_bubble_window_new (GTK_WIDGET (entry));
-  _gtk_bubble_window_set_position (GTK_BUBBLE_WINDOW (priv->selection_bubble),
-                                   GTK_POS_TOP);
+  priv->selection_bubble = gtk_popover_new (GTK_WIDGET (entry));
+  gtk_popover_set_position (GTK_POPOVER (priv->selection_bubble),
+                            GTK_POS_TOP);
 
   toolbar = GTK_WIDGET (gtk_toolbar_new ());
   gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_TEXT);
@@ -9456,8 +9456,7 @@ bubble_targets_received (GtkClipboard     *clipboard,
       rect.width = 0;
     }
 
-  _gtk_bubble_window_set_pointing_to (GTK_BUBBLE_WINDOW (priv->selection_bubble),
-                                      &rect);
+  gtk_popover_set_pointing_to (GTK_POPOVER (priv->selection_bubble), &rect);
   gtk_widget_show (priv->selection_bubble);
 
   priv->selection_bubble_timeout_id = 0;
diff --git a/gtk/gtkpopover.c b/gtk/gtkpopover.c
new file mode 100644 (file)
index 0000000..0a9d83d
--- /dev/null
@@ -0,0 +1,1050 @@
+/* GTK - The GIMP Toolkit
+ * Copyright © 2013 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * GtkPopover is a bubble-like context window, primarily mean for
+ * context-dependent helpers on touch interfaces.
+ *
+ * In order to place a GtkPopover to point to some other area,
+ * use gtk_popover_set_relative_to(), gtk_popover_set_pointing_to()
+ * and gtk_popover_set_position(). Although it is usually  more
+ * convenient to use gtk_popover_popup() which handles all of those
+ * at once.
+ *
+ * By default, no grabs are performed on the GtkPopover, leaving
+ * the popup/popdown semantics up to the caller, gtk_popover_grab()
+ * can be used to grab the window for a device pair, bringing #GtkMenu alike
+ * popdown behavior by default on keyboard/pointer interaction. Grabs need
+ * to be undone through gtk_popover_ungrab().
+ */
+
+#include "config.h"
+#include <gdk/gdk.h>
+#include <cairo-gobject.h>
+#include "gtkpopover.h"
+#include "gtktypebuiltins.h"
+#include "gtkmain.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
+
+#define TAIL_GAP_WIDTH 24
+#define TAIL_HEIGHT    12
+
+#define POS_IS_VERTICAL(p) ((p) == GTK_POS_TOP || (p) == GTK_POS_BOTTOM)
+
+typedef struct _GtkPopoverPrivate GtkPopoverPrivate;
+
+enum {
+  PROP_RELATIVE_TO = 1,
+  PROP_POINTING_TO,
+  PROP_POSITION
+};
+
+struct _GtkPopoverPrivate
+{
+  GdkDevice *device;
+  GtkWidget *widget;
+  GtkWindow *window;
+  cairo_rectangle_int_t pointing_to;
+  guint has_pointing_to    : 1;
+  guint preferred_position : 2;
+  guint final_position     : 2;
+  guint current_position   : 2;
+};
+
+static void gtk_popover_update_position    (GtkPopover *popover);
+
+G_DEFINE_TYPE_WITH_PRIVATE (GtkPopover, gtk_popover, GTK_TYPE_BIN)
+
+static void
+gtk_popover_init (GtkPopover *popover)
+{
+  GtkWidget *widget;
+
+  widget = GTK_WIDGET (popover);
+  gtk_widget_set_has_window (widget, TRUE);
+  popover->priv = gtk_popover_get_instance_private (popover);
+  gtk_style_context_add_class (gtk_widget_get_style_context (widget),
+                               GTK_STYLE_CLASS_OSD);
+}
+
+static void
+gtk_popover_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+  switch (prop_id)
+    {
+    case PROP_RELATIVE_TO:
+      gtk_popover_set_relative_to (GTK_POPOVER (object),
+                                   g_value_get_object (value));
+      break;
+    case PROP_POINTING_TO:
+      gtk_popover_set_pointing_to (GTK_POPOVER (object),
+                                   g_value_get_boxed (value));
+      break;
+    case PROP_POSITION:
+      gtk_popover_set_position (GTK_POPOVER (object),
+                                g_value_get_enum (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_popover_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+  GtkPopoverPrivate *priv = GTK_POPOVER (object)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_RELATIVE_TO:
+      g_value_set_object (value, priv->widget);
+      break;
+    case PROP_POINTING_TO:
+      g_value_set_boxed (value, &priv->pointing_to);
+      break;
+    case PROP_POSITION:
+      g_value_set_enum (value, priv->preferred_position);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gtk_popover_finalize (GObject *object)
+{
+  GtkPopover *popover = GTK_POPOVER (object);
+
+  G_OBJECT_CLASS (gtk_popover_parent_class)->finalize (object);
+}
+
+static void
+gtk_popover_dispose (GObject *object)
+{
+  GtkPopover *popover = GTK_POPOVER (object);
+  GtkPopoverPrivate *priv = popover->priv;
+
+  if (priv->window)
+    gtk_window_remove_popover (priv->window, GTK_WIDGET (object));
+
+  priv->window = NULL;
+  priv->widget = NULL;
+
+  G_OBJECT_CLASS (gtk_popover_parent_class)->dispose (object);
+}
+
+static void
+gtk_popover_realize (GtkWidget *widget)
+{
+  GtkAllocation allocation;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+  GdkWindow *window;
+
+  gtk_widget_get_allocation (widget, &allocation);
+
+  attributes.x = 0;
+  attributes.y = 0;
+  attributes.width = allocation.width;
+  attributes.height = allocation.height;
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.visual = gtk_widget_get_visual (widget);
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.event_mask =
+    gtk_widget_get_events (widget) |
+    GDK_BUTTON_MOTION_MASK |
+    GDK_BUTTON_PRESS_MASK |
+    GDK_BUTTON_RELEASE_MASK |
+    GDK_EXPOSURE_MASK |
+    GDK_ENTER_NOTIFY_MASK |
+    GDK_LEAVE_NOTIFY_MASK;
+
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+  window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                           &attributes, attributes_mask);
+  gtk_widget_set_window (widget, window);
+  gtk_widget_register_window (widget, window);
+  gtk_widget_set_realized (widget, TRUE);
+}
+
+static void
+gtk_popover_map (GtkWidget *widget)
+{
+  GTK_WIDGET_CLASS (gtk_popover_parent_class)->map (widget);
+  gdk_window_show (gtk_widget_get_window (widget));
+  gtk_popover_update_position (GTK_POPOVER (widget));
+}
+
+static void
+gtk_popover_unmap (GtkWidget *widget)
+{
+  gdk_window_hide (gtk_widget_get_window (widget));
+  GTK_WIDGET_CLASS (gtk_popover_parent_class)->unmap (widget);
+}
+
+static void
+gtk_popover_get_pointed_to_coords (GtkPopover            *popover,
+                                   cairo_rectangle_int_t *rect_out)
+{
+  GtkPopoverPrivate *priv = popover->priv;
+  cairo_rectangle_int_t rect;
+
+  if (!rect_out)
+    return;
+
+  gtk_popover_get_pointing_to (popover, &rect);
+  gtk_widget_translate_coordinates (priv->widget, GTK_WIDGET (priv->window),
+                                    rect.x, rect.y, &rect.x, &rect.y);
+  *rect_out = rect;
+}
+
+static void
+gtk_popover_get_gap_coords (GtkPopover      *popover,
+                            gint            *initial_x_out,
+                            gint            *initial_y_out,
+                            gint            *tip_x_out,
+                            gint            *tip_y_out,
+                            gint            *final_x_out,
+                            gint            *final_y_out,
+                            GtkPositionType *gap_side_out)
+{
+  GtkWidget *widget = GTK_WIDGET (popover);
+  GtkPopoverPrivate *priv = popover->priv;
+  cairo_rectangle_int_t rect;
+  gint base, tip, tip_pos;
+  gint initial_x, initial_y;
+  gint tip_x, tip_y;
+  gint final_x, final_y;
+  GtkPositionType gap_side;
+  GtkAllocation allocation;
+  gint border_radius;
+
+  gtk_popover_get_pointing_to (popover, &rect);
+  gtk_widget_get_allocation (widget, &allocation);
+  gtk_widget_translate_coordinates (priv->widget, widget,
+                                    rect.x, rect.y, &rect.x, &rect.y);
+
+  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+    rect.x += gtk_widget_get_margin_start (widget);
+  else
+    rect.x += gtk_widget_get_margin_end (widget);
+
+  rect.y += gtk_widget_get_margin_top (widget);
+
+  gtk_style_context_get (gtk_widget_get_style_context (GTK_WIDGET (popover)),
+                         gtk_widget_get_state_flags (GTK_WIDGET (popover)),
+                         GTK_STYLE_PROPERTY_BORDER_RADIUS, &border_radius,
+                         NULL);
+
+  if (priv->final_position == GTK_POS_BOTTOM ||
+      priv->final_position == GTK_POS_RIGHT)
+    {
+      base = TAIL_HEIGHT;
+      tip = 0;
+      gap_side = (priv->final_position == GTK_POS_BOTTOM) ? GTK_POS_TOP : GTK_POS_LEFT;
+    }
+  else if (priv->final_position == GTK_POS_TOP)
+    {
+      base = allocation.height - TAIL_HEIGHT;
+      tip = allocation.height;
+      gap_side = GTK_POS_BOTTOM;
+    }
+  else if (priv->final_position == GTK_POS_LEFT)
+    {
+      base = allocation.width - TAIL_HEIGHT;
+      tip = allocation.width;
+      gap_side = GTK_POS_RIGHT;
+    }
+  else
+    g_assert_not_reached ();
+
+  if (POS_IS_VERTICAL (priv->final_position))
+    {
+      tip_pos = rect.x + (rect.width / 2);
+      initial_x = CLAMP (tip_pos - TAIL_GAP_WIDTH / 2,
+                         border_radius, allocation.width - TAIL_GAP_WIDTH);
+      initial_y = base;
+
+      tip_x = CLAMP (tip_pos, 0, allocation.width);
+      tip_y = tip;
+
+      final_x = CLAMP (tip_pos + TAIL_GAP_WIDTH / 2,
+                       TAIL_GAP_WIDTH, allocation.width - (2 * border_radius));
+      final_y = base;
+    }
+  else
+    {
+      tip_pos = rect.y + (rect.height / 2);
+
+      initial_x = base;
+      initial_y = CLAMP (tip_pos - TAIL_GAP_WIDTH / 2,
+                         border_radius, allocation.height - TAIL_GAP_WIDTH);
+
+      tip_x = tip;
+      tip_y = CLAMP (tip_pos, 0, allocation.height);
+
+      final_x = base;
+      final_y = CLAMP (tip_pos + TAIL_GAP_WIDTH / 2,
+                       TAIL_GAP_WIDTH, allocation.height - (2 * border_radius));
+    }
+
+  if (initial_x_out)
+    *initial_x_out = initial_x;
+  if (initial_y_out)
+    *initial_y_out = initial_y;
+
+  if (tip_x_out)
+    *tip_x_out = tip_x;
+  if (tip_y_out)
+    *tip_y_out = tip_y;
+
+  if (final_x_out)
+    *final_x_out = final_x;
+  if (final_y_out)
+    *final_y_out = final_y;
+
+  if (gap_side_out)
+    *gap_side_out = gap_side;
+}
+
+static void
+gtk_popover_get_rect_coords (GtkPopover *popover,
+                             gint       *x1_out,
+                             gint       *y1_out,
+                             gint       *x2_out,
+                             gint       *y2_out)
+{
+  GtkWidget *widget = GTK_WIDGET (popover);
+  GtkPopoverPrivate *priv = popover->priv;
+  GtkAllocation allocation;
+  gint x1, x2, y1, y2;
+
+  gtk_widget_get_allocation (widget, &allocation);
+
+  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+    x1 = gtk_widget_get_margin_start (widget);
+  else
+    x1 = gtk_widget_get_margin_end (widget);
+
+  y1 = gtk_widget_get_margin_top (widget);
+  x2 = allocation.width -
+    gtk_widget_get_margin_end (widget) + x1;
+  y2 = allocation.height -
+    gtk_widget_get_margin_bottom (widget) + y1;
+
+  if (priv->final_position == GTK_POS_TOP)
+    y2 -= TAIL_HEIGHT;
+  else if (priv->final_position == GTK_POS_BOTTOM)
+    y1 += TAIL_HEIGHT;
+  else if (priv->final_position == GTK_POS_LEFT)
+    x2 -= TAIL_HEIGHT;
+  else if (priv->final_position == GTK_POS_RIGHT)
+    x1 += TAIL_HEIGHT;
+
+  if (x1_out)
+    *x1_out = x1;
+  if (y1_out)
+    *y1_out = y1;
+  if (x2_out)
+    *x2_out = x2;
+  if (y2_out)
+    *y2_out = y2;
+}
+
+static void
+gtk_popover_apply_tail_path (GtkPopover *popover,
+                             cairo_t    *cr)
+{
+  gint initial_x, initial_y;
+  gint tip_x, tip_y;
+  gint final_x, final_y;
+
+  gtk_popover_get_gap_coords (popover,
+                              &initial_x, &initial_y,
+                              &tip_x, &tip_y,
+                              &final_x, &final_y,
+                              NULL);
+
+  cairo_move_to (cr, initial_x, initial_y);
+  cairo_line_to (cr, tip_x, tip_y);
+  cairo_line_to (cr, final_x, final_y);
+}
+
+static void
+gtk_popover_apply_border_path (GtkPopover *popover,
+                               cairo_t    *cr)
+{
+  GtkPopoverPrivate *priv;
+  GtkAllocation allocation;
+  gint x1, y1, x2, y2;
+
+  priv = popover->priv;
+  gtk_widget_get_allocation (GTK_WIDGET (popover), &allocation);
+
+  gtk_popover_apply_tail_path (popover, cr);
+  gtk_popover_get_rect_coords (popover, &x1, &y1, &x2, &y2);
+
+  if (priv->final_position == GTK_POS_TOP)
+    {
+      cairo_line_to (cr, x2, y2);
+      cairo_line_to (cr, x2, y1);
+      cairo_line_to (cr, x1, y1);
+      cairo_line_to (cr, x1, y2);
+    }
+  else if (priv->final_position == GTK_POS_BOTTOM)
+    {
+      cairo_line_to (cr, x2, y1);
+      cairo_line_to (cr, x2, y2);
+      cairo_line_to (cr, x1, y2);
+      cairo_line_to (cr, x1, y1);
+    }
+  else if (priv->final_position == GTK_POS_LEFT)
+    {
+      cairo_line_to (cr, x2, y2);
+      cairo_line_to (cr, x1, y2);
+      cairo_line_to (cr, x1, y1);
+      cairo_line_to (cr, x2, y1);
+    }
+  else if (priv->final_position == GTK_POS_RIGHT)
+    {
+      cairo_line_to (cr, x1, y1);
+      cairo_line_to (cr, x2, y1);
+      cairo_line_to (cr, x2, y2);
+      cairo_line_to (cr, x1, y2);
+    }
+
+  cairo_close_path (cr);
+}
+
+static void
+gtk_popover_update_shape (GtkPopover *popover)
+{
+  cairo_surface_t *surface;
+  cairo_region_t *region;
+  GdkWindow *win;
+  cairo_t *cr;
+
+  win = gtk_widget_get_window (GTK_WIDGET (popover));
+  surface =
+    gdk_window_create_similar_surface (win,
+                                       CAIRO_CONTENT_COLOR_ALPHA,
+                                       gdk_window_get_width (win),
+                                       gdk_window_get_height (win));
+
+  cr = cairo_create (surface);
+  gtk_popover_apply_border_path (popover, cr);
+  cairo_fill (cr);
+  cairo_destroy (cr);
+
+  region = gdk_cairo_region_create_from_surface (surface);
+  cairo_surface_destroy (surface);
+
+  gtk_widget_shape_combine_region (GTK_WIDGET (popover), region);
+  cairo_region_destroy (region);
+}
+
+static void
+gtk_popover_update_position (GtkPopover *popover)
+{
+  GtkAllocation window_alloc;
+  cairo_rectangle_int_t rect;
+  GtkPopoverPrivate *priv;
+  GtkRequisition req;
+
+  priv = popover->priv;
+
+  if (!priv->window)
+    return;
+
+  gtk_widget_get_preferred_size (GTK_WIDGET (popover), NULL, &req);
+  gtk_widget_get_allocation (GTK_WIDGET (priv->window), &window_alloc);
+  priv->final_position = priv->preferred_position;
+
+  gtk_popover_get_pointed_to_coords (popover, &rect);
+
+  /* Check whether there's enough room on the
+   * preferred side, move to the opposite one if not.
+   */
+  if (priv->preferred_position == GTK_POS_TOP && rect.y < req.height)
+    priv->final_position = GTK_POS_BOTTOM;
+  else if (priv->preferred_position == GTK_POS_BOTTOM &&
+           rect.y > window_alloc.height - req.height)
+    priv->final_position = GTK_POS_TOP;
+  else if (priv->preferred_position == GTK_POS_LEFT && rect.x < req.width)
+    priv->final_position = GTK_POS_RIGHT;
+  else if (priv->preferred_position == GTK_POS_RIGHT &&
+           rect.x > window_alloc.width - req.width)
+    priv->final_position = GTK_POS_LEFT;
+
+  gtk_window_set_popover_position (priv->window, GTK_WIDGET (popover),
+                                   priv->final_position, &rect);
+
+  if (priv->final_position != priv->current_position)
+    {
+      if (gtk_widget_is_drawable (GTK_WIDGET (popover)))
+        gtk_popover_update_shape (popover);
+
+      priv->current_position = priv->final_position;
+    }
+}
+
+static gboolean
+gtk_popover_draw (GtkWidget *widget,
+                  cairo_t   *cr)
+{
+  GtkStyleContext *context;
+  GtkAllocation allocation;
+  GtkWidget *child;
+  GtkBorder border;
+  GdkRGBA border_color;
+  gint rect_x1, rect_x2, rect_y1, rect_y2;
+  gint initial_x, initial_y, final_x, final_y;
+  gint gap_start, gap_end;
+  GtkPositionType gap_side;
+  GtkStateFlags state;
+
+  context = gtk_widget_get_style_context (widget);
+  state = gtk_widget_get_state_flags (widget);
+  gtk_widget_get_allocation (widget, &allocation);
+
+  gtk_popover_get_rect_coords (GTK_POPOVER (widget),
+                               &rect_x1, &rect_y1,
+                               &rect_x2, &rect_y2);
+
+  /* Render the rect background */
+  gtk_render_background (context, cr,
+                         rect_x1, rect_y1,
+                         rect_x2 - rect_x1, rect_y2 - rect_y1);
+
+  gtk_popover_get_gap_coords (GTK_POPOVER (widget),
+                              &initial_x, &initial_y,
+                              NULL, NULL,
+                              &final_x, &final_y,
+                              &gap_side);
+
+  if (POS_IS_VERTICAL (gap_side))
+    {
+      gap_start = initial_x;
+      gap_end = final_x;
+    }
+  else
+    {
+      gap_start = initial_y;
+      gap_end = final_y;
+    }
+
+  /* Now render the frame, without the gap for the arrow tip */
+  gtk_render_frame_gap (context, cr,
+                        rect_x1, rect_y1,
+                        rect_x2 - rect_x1, rect_y2 - rect_y1,
+                        gap_side,
+                        gap_start, gap_end);
+
+  /* Clip to the arrow shape */
+  cairo_save (cr);
+
+  gtk_popover_apply_tail_path (GTK_POPOVER (widget), cr);
+  cairo_clip (cr);
+
+  /* Render the arrow background */
+  gtk_render_background (context, cr,
+                         0, 0,
+                         allocation.width, allocation.height);
+
+  /* Render the border of the arrow tip */
+  gtk_style_context_get_border (context, state, &border);
+
+  if (border.bottom > 0)
+    {
+      gtk_style_context_get_border_color (context, state, &border_color);
+      gtk_popover_apply_tail_path (GTK_POPOVER (widget), cr);
+      gdk_cairo_set_source_rgba (cr, &border_color);
+
+      cairo_set_line_width (cr, border.bottom);
+      cairo_stroke (cr);
+    }
+
+  /* We're done */
+  cairo_restore (cr);
+
+  child = gtk_bin_get_child (GTK_BIN (widget));
+
+  if (child)
+    gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
+
+  return TRUE;
+}
+
+static void
+get_padding_and_border (GtkWidget *widget,
+                        GtkBorder *border)
+{
+  GtkStyleContext *context;
+  GtkStateFlags state;
+  GtkBorder tmp;
+
+  context = gtk_widget_get_style_context (widget);
+  state = gtk_widget_get_state_flags (widget);
+
+  gtk_style_context_get_padding (context, state, border);
+  gtk_style_context_get_border (context, state, &tmp);
+  border->top += tmp.top;
+  border->right += tmp.right;
+  border->bottom += tmp.bottom;
+  border->left += tmp.left;
+}
+
+static void
+gtk_popover_get_preferred_width (GtkWidget *widget,
+                                 gint      *minimum_width,
+                                 gint      *natural_width)
+{
+  GtkPopoverPrivate *priv;
+  GtkWidget *child;
+  gint min, nat;
+  GtkBorder border;
+
+  priv = GTK_POPOVER (widget)->priv;
+  child = gtk_bin_get_child (GTK_BIN (widget));
+  min = nat = 0;
+
+  if (child)
+    gtk_widget_get_preferred_width (child, &min, &nat);
+
+  get_padding_and_border (widget, &border);
+  min += border.left + border.right;
+  nat += border.left + border.right;
+
+  if (!POS_IS_VERTICAL (priv->preferred_position))
+    {
+      min += TAIL_HEIGHT;
+      nat += TAIL_HEIGHT;
+    }
+
+  if (minimum_width)
+    *minimum_width = MAX (min, TAIL_GAP_WIDTH);
+
+  if (natural_width)
+    *natural_width = MAX (nat, TAIL_GAP_WIDTH);
+}
+
+static void
+gtk_popover_get_preferred_height (GtkWidget *widget,
+                                  gint      *minimum_height,
+                                  gint      *natural_height)
+{
+  GtkPopoverPrivate *priv;
+  GtkWidget *child;
+  gint min, nat;
+  GtkBorder border;
+
+  priv = GTK_POPOVER (widget)->priv;
+  child = gtk_bin_get_child (GTK_BIN (widget));
+  min = nat = 0;
+
+  if (child)
+    gtk_widget_get_preferred_height (child, &min, &nat);
+
+  get_padding_and_border (widget, &border);
+  min += border.top + border.bottom;
+  nat += border.top + border.bottom;
+
+  if (POS_IS_VERTICAL (priv->preferred_position))
+    {
+      min += TAIL_HEIGHT;
+      nat += TAIL_HEIGHT;
+    }
+
+  if (minimum_height)
+    *minimum_height = MAX (min, TAIL_GAP_WIDTH);
+
+  if (natural_height)
+    *natural_height = MAX (nat, TAIL_GAP_WIDTH);
+}
+
+static void
+gtk_popover_size_allocate (GtkWidget     *widget,
+                           GtkAllocation *allocation)
+{
+  GtkWidget *child;
+
+  gtk_widget_set_allocation (widget, allocation);
+  child = gtk_bin_get_child (GTK_BIN (widget));
+
+  if (child)
+    {
+      GtkAllocation child_alloc;
+      gint x1, y1, x2, y2;
+      GtkBorder border;
+
+      gtk_popover_get_rect_coords (GTK_POPOVER (widget),
+                                   &x1, &y1, &x2, &y2);
+      get_padding_and_border (widget, &border);
+
+      child_alloc.x = x1 + border.left;
+      child_alloc.y = y1 + border.top;
+      child_alloc.width = (x2 - x1) - border.left - border.right;
+      child_alloc.height = (y2 - y1) - border.top - border.bottom;
+      gtk_widget_size_allocate (child, &child_alloc);
+    }
+
+  if (gtk_widget_get_realized (widget))
+    {
+      gdk_window_move_resize (gtk_widget_get_window (widget),
+                              0, 0, allocation->width, allocation->height);
+      gtk_popover_update_shape (GTK_POPOVER (widget));
+    }
+}
+
+static gboolean
+gtk_popover_button_press (GtkWidget      *widget,
+                          GdkEventButton *event)
+{
+  GtkWidget *child;
+
+  child = gtk_bin_get_child (GTK_BIN (widget));
+
+  if (child && event->window == gtk_widget_get_window (widget))
+    {
+      GtkAllocation child_alloc;
+
+      gtk_widget_get_allocation (child, &child_alloc);
+
+      if (event->x < child_alloc.x ||
+          event->x > child_alloc.x + child_alloc.width ||
+          event->y < child_alloc.y ||
+          event->y > child_alloc.y + child_alloc.height)
+        gtk_widget_hide (widget);
+    }
+  else
+    gtk_widget_hide (widget);
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static gboolean
+gtk_popover_key_press (GtkWidget   *widget,
+                       GdkEventKey *event)
+{
+  if (event->keyval == GDK_KEY_Escape)
+    {
+      gtk_widget_hide (widget);
+      return GDK_EVENT_STOP;
+    }
+
+  return GDK_EVENT_PROPAGATE;
+}
+
+static void
+gtk_popover_class_init (GtkPopoverClass *klass)
+{
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = gtk_popover_set_property;
+  object_class->get_property = gtk_popover_get_property;
+  object_class->finalize = gtk_popover_finalize;
+  object_class->dispose = gtk_popover_dispose;
+
+  widget_class->realize = gtk_popover_realize;
+  widget_class->map = gtk_popover_map;
+  widget_class->unmap = gtk_popover_unmap;
+  widget_class->get_preferred_width = gtk_popover_get_preferred_width;
+  widget_class->get_preferred_height = gtk_popover_get_preferred_height;
+  widget_class->size_allocate = gtk_popover_size_allocate;
+  widget_class->draw = gtk_popover_draw;
+  widget_class->button_press_event = gtk_popover_button_press;
+  widget_class->key_press_event = gtk_popover_key_press;
+
+  g_object_class_install_property (object_class,
+                                   PROP_RELATIVE_TO,
+                                   g_param_spec_object ("relative-to",
+                                                        P_("Relative to"),
+                                                        P_("Window the bubble window points to"),
+                                                        GTK_TYPE_WIDGET,
+                                                        GTK_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+                                   PROP_POINTING_TO,
+                                   g_param_spec_boxed ("pointing-to",
+                                                       P_("Pointing to"),
+                                                       P_("Rectangle the bubble window points to"),
+                                                       CAIRO_GOBJECT_TYPE_RECTANGLE_INT,
+                                                       GTK_PARAM_READWRITE));
+  g_object_class_install_property (object_class,
+                                   PROP_POSITION,
+                                   g_param_spec_enum ("position",
+                                                      P_("Position"),
+                                                      P_("Position to place the bubble window"),
+                                                      GTK_TYPE_POSITION_TYPE, GTK_POS_TOP,
+                                                      GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static void
+_gtk_popover_update_context_parent (GtkPopover *popover)
+{
+  GtkStyleContext *context, *parent_context = NULL;
+  GtkPopoverPrivate *priv = popover->priv;
+
+  context = gtk_widget_get_style_context (GTK_WIDGET (popover));
+
+  if (priv->widget)
+    parent_context = gtk_widget_get_style_context (priv->widget);
+
+  gtk_style_context_set_parent (context, parent_context);
+}
+
+static void
+gtk_popover_update_relative_to (GtkPopover *popover,
+                                GtkWidget  *relative_to)
+{
+  GtkPopoverPrivate *priv;
+
+  priv = popover->priv;
+
+  if (priv->widget == relative_to)
+    return;
+
+  if (priv->window)
+    {
+      gtk_window_remove_popover (priv->window, GTK_WIDGET (popover));
+      priv->window = NULL;
+    }
+
+  priv->widget = relative_to;
+  g_object_notify (G_OBJECT (popover), "relative-to");
+
+  if (priv->widget)
+    priv->window =
+      GTK_WINDOW (gtk_widget_get_ancestor (priv->widget, GTK_TYPE_WINDOW));
+
+  if (priv->window)
+    gtk_window_add_popover (priv->window, GTK_WIDGET (popover));
+
+  _gtk_popover_update_context_parent (popover);
+}
+
+static void
+gtk_popover_update_pointing_to (GtkPopover            *popover,
+                                cairo_rectangle_int_t *pointing_to)
+{
+  GtkPopoverPrivate *priv;
+
+  priv = popover->priv;
+
+  if (pointing_to)
+    {
+      priv->pointing_to = *pointing_to;
+      priv->has_pointing_to = TRUE;
+    }
+  else
+    priv->has_pointing_to = FALSE;
+
+  g_object_notify (G_OBJECT (popover), "pointing-to");
+}
+
+static void
+gtk_popover_update_preferred_position (GtkPopover      *popover,
+                                       GtkPositionType  position)
+{
+  GtkPopoverPrivate *priv;
+
+  priv = popover->priv;
+  priv->preferred_position = position;
+  g_object_notify (G_OBJECT (popover), "position");
+}
+
+/*
+ * gtk_popover_new:
+ *
+ * Returns a newly created #GtkBubblewindow
+ *
+ * Returns: a new #GtkPopover
+ *
+ * Since: 3.8
+ */
+GtkWidget *
+gtk_popover_new (GtkWidget *relative_to)
+{
+  return g_object_new (GTK_TYPE_POPOVER,
+                       "relative-to", relative_to,
+                       NULL);
+}
+
+/*
+ * gtk_popover_set_relative_to:
+ * @window: a #GtkPopover
+ * @relative_to: a #GdkWindow
+ *
+ * Sets the #GdkWindow to act as the origin of coordinates of
+ * @window, or %NULL to use the root window. See
+ * gtk_popover_set_pointing_to()
+ *
+ * If @window is currently visible, it will be moved to reflect
+ * this change.
+ *
+ * Since: 3.8
+ */
+void
+gtk_popover_set_relative_to (GtkPopover *popover,
+                             GtkWidget  *relative_to)
+{
+  g_return_if_fail (GTK_IS_POPOVER (popover));
+  g_return_if_fail (GTK_IS_WIDGET (relative_to));
+
+  gtk_popover_update_relative_to (popover, relative_to);
+  gtk_popover_update_position (popover);
+}
+
+/*
+ * gtk_popover_get_relative_to:
+ * @window: a #GtkPopover
+ *
+ * Returns the #GdkWindow used as the origin of coordinates.
+ * If @window is currently visible, it will be moved to reflect
+ * this change.
+ *
+ * Returns: the #GdkWindow @window is placed upon
+ *
+ * Since: 3.8
+ */
+GtkWidget *
+gtk_popover_get_relative_to (GtkPopover *popover)
+{
+  GtkPopoverPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_POPOVER (popover), NULL);
+
+  priv = popover->priv;
+
+  return priv->widget;
+}
+
+/*
+ * gtk_popover_set_pointing_to:
+ * @window: a #GtkPopover
+ * @rect: rectangle to point to
+ *
+ * Sets the rectangle that @window will point to, the coordinates
+ * of this rectangle are relative to the #GdkWindow set through
+ * gtk_popover_set_relative_to().
+ *
+ * Since: 3.8
+ */
+void
+gtk_popover_set_pointing_to (GtkPopover            *popover,
+                             cairo_rectangle_int_t *rect)
+{
+  g_return_if_fail (GTK_IS_POPOVER (popover));
+  g_return_if_fail (rect != NULL);
+
+  gtk_popover_update_pointing_to (popover, rect);
+  gtk_popover_update_position (popover);
+}
+
+/*
+ * gtk_popover_get_pointing_to:
+ * @window: a #GtkPopover
+ * @rect: (out): location to store the rectangle
+ *
+ * If a rectangle to point to is set, this function will return
+ * %TRUE and fill in @rect with the rectangle @window is currently
+ * pointing to.
+ *
+ * Returns: %TRUE if a rectangle is set
+ *
+ * Since: 3.8
+ */
+gboolean
+gtk_popover_get_pointing_to (GtkPopover            *popover,
+                             cairo_rectangle_int_t *rect)
+{
+  GtkPopoverPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_POPOVER (popover), FALSE);
+
+  priv = popover->priv;
+
+  if (rect)
+    {
+      if (priv->has_pointing_to)
+        *rect = priv->pointing_to;
+      else if (priv->widget)
+        {
+          gtk_widget_get_allocation (priv->widget, rect);
+          rect->x = rect->y = 0;
+        }
+    }
+
+  return priv->has_pointing_to;
+}
+
+/*
+ * gtk_popover_set_position:
+ * @window: a #GtkPopover
+ * @position: preferred bubble position
+ *
+ * Sets the preferred position for @window to appear.
+ * If @window is currently visible, it will be moved to reflect
+ * this change.
+ *
+ * <note>
+ *   This preference will be respected where possible, although
+ *   on lack of space (eg. if close to the screen edges), the
+ *   #GtkPopover may choose to appear on the opposite side
+ * </note>
+ *
+ * Since: 3.8
+ */
+void
+gtk_popover_set_position (GtkPopover      *popover,
+                          GtkPositionType  position)
+{
+  g_return_if_fail (GTK_IS_POPOVER (popover));
+  g_return_if_fail (position >= GTK_POS_LEFT && position <= GTK_POS_BOTTOM);
+
+  gtk_popover_update_preferred_position (popover, position);
+  gtk_popover_update_position (popover);
+}
+
+/*
+ * gtk_popover_get_position:
+ * @window: a #GtkPopover
+ *
+ * Returns the preferred position to place @window
+ *
+ * Returns: The preferred position
+ *
+ * Since: 3.8
+ */
+GtkPositionType
+gtk_popover_get_position (GtkPopover *popover)
+{
+  GtkPopoverPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_POPOVER (popover), GTK_POS_TOP);
+
+  priv = popover->priv;
+
+  return priv->preferred_position;
+}
diff --git a/gtk/gtkpopover.h b/gtk/gtkpopover.h
new file mode 100644 (file)
index 0000000..231dcb0
--- /dev/null
@@ -0,0 +1,78 @@
+/* GTK - The GIMP Toolkit
+ * Copyright © 2013 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_POPOVER_H__
+#define __GTK_POPOVER_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkwindow.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_POPOVER           (gtk_popover_get_type ())
+#define GTK_POPOVER(o)             (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_POPOVER, GtkPopover))
+#define GTK_POPOVER_CLASS(c)       (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_POPOVER, GtkPopoverClass))
+#define GTK_IS_POPOVER(o)          (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_POPOVER))
+#define GTK_IS_POPOVER_CLASS(o)    (G_TYPE_CHECK_CLASS_TYPE ((o), GTK_TYPE_POPOVER))
+#define GTK_POPOVER_GET_CLASS(o)   (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_POPOVER, GtkPopoverClass))
+
+typedef struct _GtkPopover GtkPopover;
+typedef struct _GtkPopoverClass GtkPopoverClass;
+
+struct _GtkPopover
+{
+  GtkBin parent_instance;
+
+  /*< private >*/
+  gpointer priv;
+};
+
+struct _GtkPopoverClass
+{
+  GtkBinClass parent_class;
+};
+
+GDK_AVAILABLE_IN_3_12
+GType           gtk_popover_get_type        (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_12
+GtkWidget *     gtk_popover_new             (GtkWidget             *relative_to);
+
+GDK_AVAILABLE_IN_3_12
+void            gtk_popover_set_relative_to (GtkPopover            *popover,
+                                             GtkWidget             *relative_to);
+GDK_AVAILABLE_IN_3_12
+GtkWidget *     gtk_popover_get_relative_to (GtkPopover            *popover);
+
+GDK_AVAILABLE_IN_3_12
+void            gtk_popover_set_pointing_to (GtkPopover            *popover,
+                                             cairo_rectangle_int_t *rect);
+GDK_AVAILABLE_IN_3_12
+gboolean        gtk_popover_get_pointing_to (GtkPopover            *popover,
+                                             cairo_rectangle_int_t *rect);
+GDK_AVAILABLE_IN_3_12
+void            gtk_popover_set_position    (GtkPopover            *popover,
+                                             GtkPositionType        position);
+GDK_AVAILABLE_IN_3_12
+GtkPositionType gtk_popover_get_position    (GtkPopover            *popover);
+
+G_END_DECLS
+
+#endif /* __GTK_POPOVER_H__ */
index 36265c00120ccd3b73997aacf4620d28a9ea433c..2223ba27b1201b50f3739f4ea8e928fb18b78708 100644 (file)
@@ -51,7 +51,7 @@
 #include "gtktexthandleprivate.h"
 #include "gtkstylecontextprivate.h"
 #include "gtkcssstylepropertyprivate.h"
-#include "gtkbubblewindowprivate.h"
+#include "gtkpopover.h"
 #include "gtktoolbar.h"
 #include "gtkpixelcacheprivate.h"
 
@@ -8901,9 +8901,9 @@ bubble_targets_received (GtkClipboard     *clipboard,
   if (priv->selection_bubble)
     gtk_widget_destroy (priv->selection_bubble);
 
-  priv->selection_bubble = _gtk_bubble_window_new (GTK_WIDGET (text_view));
-  _gtk_bubble_window_set_position (GTK_BUBBLE_WINDOW (priv->selection_bubble),
-                                   GTK_POS_TOP);
+  priv->selection_bubble = gtk_popover_new (GTK_WIDGET (text_view));
+  gtk_popover_set_position (GTK_POPOVER (priv->selection_bubble),
+                            GTK_POS_TOP);
 
   toolbar = GTK_WIDGET (gtk_toolbar_new ());
   gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_TEXT);
@@ -8932,8 +8932,7 @@ bubble_targets_received (GtkClipboard     *clipboard,
   rect.x -= priv->xoffset;
   rect.y -= priv->yoffset;
 
-  _gtk_bubble_window_set_pointing_to (GTK_BUBBLE_WINDOW (priv->selection_bubble),
-                                      &rect);
+  gtk_popover_set_pointing_to (GTK_POPOVER (priv->selection_bubble), &rect);
   gtk_widget_show (priv->selection_bubble);
 
   priv->selection_bubble_timeout_id = 0;